package beans;

import java.io.StreamCorruptedException;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

import ourImage.OurPlanarImage;
import pimpmypipe.filter.AbstractFilter;
import pimpmypipe.interfaces.Readable;
import Listeners.ImageListener;
import events.ImageEvent;

/**
 * Base for all filters (excluding source and sink)
 * 
 * This base provides the event handling mechanism. 
 * 
 * T : generic --> Concrete Filter
 * 
 * @author Marcel Meraner
 */
public abstract class FilterBaseBean<T extends AbstractFilter<OurPlanarImage, OurPlanarImage>> extends BaseBean implements ImageListener {

    protected OurPlanarImage entity;
    protected LinkedList<ImageListener> listeners = new LinkedList<>();
    protected T filter;
    protected OurPlanarImage entityFromSource;
   
    
    /*
     * IO Methods (from IOable interface)
     * 
     */
    @Override
    public void write(OurPlanarImage value) throws StreamCorruptedException {
        this.entity = value;
    }
    
    @Override
    public OurPlanarImage read() throws StreamCorruptedException {
        return this.entity.clone();
    }
    
    /*
     * BeanBox Events
     *
     */
    @Override
    public void onImageProceed(ImageEvent e) {
        // Proceed event
        BaseBean src = (BaseBean)e.getSource();
        if (filter == null) {
            filter = this.getConcreteFilter(src);
        }
        try {
            // Fetch Source Entity (Image) and filter ist
            entityFromSource = src.read();
            filteringAndNotify();
            
        } catch (StreamCorruptedException ex) {
            Logger.getLogger(FilterBaseBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void addImageListener(ImageListener listener) {
        listeners.add(listener);
    }
    public void removeImageListener(ImageListener listener) {
        listeners.remove(listener);
    }
    protected void notifyImageListener() {
        LinkedList<ImageListener> listens;
        ImageEvent event = new ImageEvent(this);
        synchronized(this) {
            listens = (LinkedList<ImageListener>) listeners.clone();
        }
        for (ImageListener l : listens) {
            l.onImageProceed(event);
        }
    }

    
    /**
     * This methods calls the real filter and notify all listeners after filtering
     */
    protected void filteringAndNotify() {
        if (entityFromSource != null) {
            try {
                // Reconfigure filter
                filter = configureConcreteFilter(filter);
                // Call filter
                filter.write(entityFromSource.clone());
                // Notify all listeners
                notifyImageListener();
                
            } catch (StreamCorruptedException ex) {
                Logger.getLogger(FilterBaseBean.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        }
    }
    
    
    /**
     * Returns the concrete Filter
     * 
     * @param in
     * @return 
     */
    protected abstract T getConcreteFilter(Readable<OurPlanarImage> in);
    
    /**
     * Configure the concrete filter
     * 
     * @param filter - filter to configure
     * @return 
     */
    protected abstract T configureConcreteFilter(T filter);
    
}
